<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\workflow;

use lflow\access\QueryFilter;
use lflow\entity\CCOrder;
use lflow\entity\HistoryOrder;
use lflow\entity\HistoryTask;
use lflow\entity\HistoryTaskActor;
use lflow\entity\Order;
use lflow\entity\Process;
use lflow\entity\Task;
use lflow\helper\ArrayHelper;
use lflow\helper\AssertHelper;
use lflow\helper\DateHelper;
use lflow\helper\JsonHelper;
use lflow\helper\ObjectHelper;
use lflow\helper\StringHelper;
use lflow\IOrderService;

/**
 * 流程实例业务类
 *
 * @author Mr.April
 * @since  1.0
 */
class OrderService extends AccessService implements IOrderService
{

    public function createOrder(Process $process, string $operator, string|object $args, string $parentId = '', string $parentNodeName = '', int $priority = 1): Order
    {
        $order = new Order();
        $order->setId(StringHelper::getPrimaryKey());
        $order->setProcessId($process->getId());
        $order->setParentId($parentId ?? '');
        $order->setParentNodeName($parentNodeName);
        $order->setCreateTime(DateHelper::getTime());
        $order->setLastUpdateTime($order->getCreateTime());
        $order->setCreator($operator);
        $order->setLastUpdator($order->getCreator());
        $model = $process->getModel();
        if ($model != null && $args != null) {
            if (StringHelper::isNotEmpty($model->getExpireTime())) {
                $expireTime = DateHelper::parseTime($args->get($model->getExpireTime()));
                $order->setExpireTime($expireTime);
            }
            $args    = JsonHelper::fromJson($args, false);
            $orderNo = $args->orderNo ?? '';
            if (StringHelper::isNotEmpty($orderNo)) {
                $order->setOrderNo($orderNo);
            } else {
                $order->setOrderNo($model->getGenerator()->generate($model));
            }
        }
        $order->setVariable(json_encode($args));
        $this->saveOrder($order);
        return $order;
    }

    public function addVariable(string $orderId, string $args): void
    {
        AssertHelper::notNull($orderId);
        $order = $this->access()->getOrder($orderId);
        $data  = ObjectHelper::putAll(json_decode($order->getVariable(), false), json_decode($args, false));
        $order->setVariable(json_encode($data));
        $this->access()->updateOrderVariable($order);
    }

    public function createCCOrder(string $orderId, string $creator, string $actorIds): void
    {
        foreach (explode(',', $actorIds) as $actorId) {
            $ccorder = new CCOrder();
            $ccorder->setOrderId($orderId);
            $ccorder->setActorId($actorId);
            $ccorder->setCreator($creator);
            $ccorder->setStatus(self::STATE_ACTIVE);
            $ccorder->setCreateTime(DateHelper::getTime());
            $this->access()->saveCCOrder($ccorder);
        }
    }

    public function saveOrder(Order $order): void
    {

        $history = new HistoryOrder();
        $history->historyOrder($order);
        $history->setOrderState(self::STATE_ACTIVE);
        $this->access()->saveOrder($order);
        $this->access()->saveHistory($history);
    }

    public function updateOrder(Order $order): void
    {
        $this->access()->updateOrder($order);
    }

    public function updateCCStatus(string $orderId, string $actorIds): void
    {
        $ccorders = $this->access()->getCCOrder($orderId, $actorIds);
        AssertHelper::notNull($ccorders);
        foreach ($ccorders as $ccorder) {
            $ccorder->setStatus(self::STATE_FINISH);
            $ccorder->setFinishTime(DateHelper:: getTime());
            $this->access()->updateCCOrder($ccorder);
        }

    }

    public function deleteCCOrder(string $orderId, string $actorId): void
    {
        $ccorders = $this->access()->getCCOrder($orderId, $actorId);
        AssertHelper::notNull($ccorders);
        foreach ($ccorders as $ccorder) {
            $this->access()->deleteCCOrder($ccorder);
        }
    }

    public function complete(string $orderId): void
    {
        $order   = $this->access()->getOrder($orderId);
        $history = $this->access()->getHistOrder($orderId);
        $history->setOrderState(self::STATE_FINISH);
        $history->setEndTime(DateHelper:: getTime());
        $this->access()->updateHistory($history);
        $this->access()->deleteOrder($order);
    }

    public function terminate(string $orderId, string $operator): void
    {

    }

    public function resume(string $orderId): Order
    {
        $historyOrder = $this->access()->getHistOrder($orderId);
        $order        = $historyOrder->undo();
        $this->access()->saveOrder($order);
        $historyOrder->setOrderState(self::STATE_ACTIVE);
        $this->access()->updateHistory($historyOrder);
        return $order;
    }

    public function cascadeRemove(string $id): void
    {
        $historyOrder = $this->access()->getHistOrder($id);
        AssertHelper:: notNull($historyOrder);
        $activeTasks  = $this->access()->getActiveTasks((new QueryFilter())->setOrderId($id), null);
        $historyTasks = $this->access()->getHistoryTasks((new QueryFilter())->setOrderId($id), null);
        foreach ($activeTasks as $task) {
            $value = ArrayHelper::arrayToSimpleObj($task, Task::class);
            $this->access()->deleteTask($value);
        }
        foreach ($historyTasks as $historyTask) {
            $value = ArrayHelper::arrayToSimpleObj($historyTask, HistoryTask::class);
            $this->access()->deleteHistoryTask($value);
        }
        $ccOrders = $this->access()->getCCOrder($id);
        foreach ($ccOrders as $ccOrder) {
            $value = ArrayHelper::arrayToSimpleObj($ccOrder, CCOrder::class);
            $this->access()->deleteCCOrder($ccOrder);
        }
        $order = $this->access()->getOrder($id);
        $this->access()->deleteHistoryOrder($historyOrder);
        if (!empty($order)) {
            $this->access()->deleteOrder($order);
        }
    }
}
